package com.yanyeori.framework.core.util;

import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSA非对称加解密、验签工具
 *
 * @author chenkuan 2021/8/25
 */
public class RSAUtil {
    private static final String KEY_ALGORITHM = "RSA";
    private static final String SIGNATURE_ALGORITHM = "SHA1WithRSA";
    private static final int KEY_SIZE = 2048;

    private static Cipher cipher;
    private static final byte[] cipher_lock = {0};

    private static Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
        if (cipher == null) {
            cipher = Cipher.getInstance(KEY_ALGORITHM);
        }
        return cipher;
    }

    /**
     * 初始化公私钥
     */
    public static RSAKey initKey() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        return new RSAKey(Base64Utils.encodeToString(publicKey.getEncoded()), Base64Utils.encodeToString(privateKey.getEncoded()));
    }

    /**
     * 使用公钥对明文进行加密，返回BASE64编码的字符串
     *
     * @param publicKey 公钥
     * @param plainText 待加密明文
     * @return 公钥加密，BASE64编码的字符串
     */
    public static String encrypt(String publicKey, String plainText) {
        synchronized (cipher_lock) {
            try {
                PublicKey pubKey = getPublicKey(publicKey);
                getCipher().init(Cipher.ENCRYPT_MODE, pubKey);
                byte[] enBytes = getCipher().doFinal(plainText.getBytes(StandardCharsets.UTF_8));
                return Base64Utils.encodeToString(enBytes);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 使用私钥对明文密文进行解密
     *
     * @param privateKey 私钥
     * @param cipherText 公钥加密的密文
     * @return 明文字符串
     */
    public static String decrypt(String privateKey, String cipherText) {
        synchronized (cipher_lock) {
            try {
                PrivateKey priKey = getPrivateKey(privateKey);
                getCipher().init(Cipher.DECRYPT_MODE, priKey);
                byte[] deBytes = getCipher().doFinal(Base64Utils.decodeFromString(cipherText));
                return StringUtil.str(deBytes);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * RSA私钥签名
     *
     * @param content    待签名数据
     * @param privateKey 私钥
     * @return 签名值
     */
    public static String signByPrivateKey(String content, String privateKey) {
        try {
            PrivateKey priKey = getPrivateKey(privateKey);
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initSign(priKey);
            signature.update(content.getBytes(StandardCharsets.UTF_8));
            byte[] signed = signature.sign();
            return Base64Utils.encodeToString(signed);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 公钥验签
     *
     * @param content   签名数据
     * @param sign      签名值
     * @param publicKey 公钥
     * @return 验签结果
     */
    public static boolean verifySignByPublicKey(String content, String sign, String publicKey) {
        try {
            PublicKey pubKey = getPublicKey(publicKey);
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initVerify(pubKey);
            signature.update(content.getBytes(StandardCharsets.UTF_8));
            return signature.verify(Base64Utils.decodeFromString(sign));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据公钥字符串获取公钥对象
     */
    private static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes = Base64Utils.decodeFromString(key);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory.generatePublic(new X509EncodedKeySpec(keyBytes));
    }

    /**
     * 根据私钥字符串获取私钥对象
     */
    private static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] keyBytes = Base64Utils.decodeFromString(key);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
    }

    public static class RSAKey {
        private String publicKey;
        private String privateKey;

        public RSAKey() {
        }

        public RSAKey(String publicKey, String privateKey) {
            this.publicKey = publicKey;
            this.privateKey = privateKey;
        }

        public String getPublicKey() {
            return publicKey;
        }

        public void setPublicKey(String publicKey) {
            this.publicKey = publicKey;
        }

        public String getPrivateKey() {
            return privateKey;
        }

        public void setPrivateKey(String privateKey) {
            this.privateKey = privateKey;
        }
    }
}
